home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / pine / mailcap.c < prev    next >
C/C++ Source or Header  |  1996-07-15  |  33KB  |  1,249 lines

  1. #if !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: mailcap.c,v 1.44 1996/07/15 18:04:40 mikes Exp $";
  3. #endif
  4. /*----------------------------------------------------------------------
  5.  
  6.            T H E    P I N E    M A I L   S Y S T E M
  7.  
  8.    Laurence Lundblade, Mike Seibel and David Miller
  9.    Networks and Distributed Computing
  10.    Computing and Communications
  11.    University of Washington
  12.    4545 Builiding, JE-20
  13.    Seattle, Washington, 98195, USA
  14.    Internet: mikes@cac.washington.edu
  15.              dlm@cac.wasington.edu
  16.  
  17.    Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  18.  
  19.  
  20.    Pine and Pico are registered trademarks of the University of Washington.
  21.    No commercial use of these trademarks may be made without prior written
  22.    permission of the University of Washington.
  23.  
  24.    Pine, Pico, and Pilot software and its included text are Copyright
  25.    1989-1996 by the University of Washington.
  26.  
  27.    The full text of our legal notices is contained in the file called
  28.    CPYRIGHT, included with this distribution.
  29.  
  30.  
  31.    Pine is in part based on The Elm Mail System:
  32.     ***********************************************************************
  33.     *  The Elm Mail System  -  Revision: 2.13                             *
  34.     *                                                                     *
  35.     *                   Copyright (c) 1986, 1987 Dave Taylor              *
  36.     *                   Copyright (c) 1988, 1989 USENET Community Trust   *
  37.     ***********************************************************************
  38.  
  39.  
  40.    ******************************************************* 
  41.    Mailcap support is based in part on Metamail version 2.7:
  42.       Author:  Nathaniel S. Borenstein, Bellcore
  43.  
  44.    Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore)
  45.  
  46.    Permission to use, copy, modify, and distribute this material
  47.    for any purpose and without fee is hereby granted, provided
  48.    that the above copyright notice and this permission notice
  49.    appear in all copies, and that the name of Bellcore not be
  50.    used in advertising or publicity pertaining to this
  51.    material without the specific, prior written permission
  52.    of an authorized representative of Bellcore.  BELLCORE
  53.    MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY
  54.    OF THIS MATERIAL FOR ANY PURPOSE.  IT IS PROVIDED "AS IS",
  55.    WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
  56.    ******************************************************* 
  57.  
  58.    ******************************************************* 
  59.    MIME-TYPE support based on code contributed
  60.    by Hans Drexler <drexler@mpi.nl>.  His comment:
  61.  
  62.    Mime types makes mime assign attachment types according
  63.    to file name extensions found in a system wide file
  64.    ``/usr/local/lib/mime.types'' and a user specific file
  65.    ``~/.mime.types'' . These files specify file extensions
  66.    that will be connected to a mime type.
  67.    ******************************************************* 
  68.  
  69.  
  70.   ----------------------------------------------------------------------*/
  71.  
  72. /*======================================================================
  73.        mailcap.c
  74.  
  75.        Functions to support "mailcap" (RFC1524) to help us read MIME
  76.        segments of various types, and ".mime.types" ala NCSA Mosaic to
  77.        help us attach various types to outbound MIME segments.
  78.  
  79.  ====*/
  80.  
  81. #include "headers.h"
  82.  
  83. /*
  84.  * We've decided not to implement the RFC1524 standard minimum path, because
  85.  * some of us think it is harder to debug a problem when you may be misled
  86.  * into looking at the wrong mailcap entry.  Likewise for MIME.Types files.
  87.  */
  88. #if defined(DOS) || defined(OS2)
  89. #define MC_PATH_SEPARATOR ';'
  90. #define MT_PATH_SEPARATOR ';'
  91. #define    MT_USER_FILE      "mimetype"
  92. #else /* !DOS */
  93. #define MC_PATH_SEPARATOR ':'
  94. #define MC_STDPATH         \
  95.         ".mailcap:/etc/mailcap:/usr/etc/mailcap:/usr/local/etc/mailcap"
  96. #define MT_PATH_SEPARATOR ':'
  97. #define MT_STDPATH         \
  98.         ".mime.types:/etc/mime.types:/usr/local/lib/mime.types"
  99. #endif /* !DOS */
  100.  
  101. #define LINE_BUF_SIZE      2000
  102.  
  103. typedef struct mcap_entry {
  104.     struct mcap_entry *next;
  105.     int                needsterminal;
  106.     char              *contenttype;
  107.     char              *command;
  108.     char              *testcommand;
  109.     char              *label;           /* unused */
  110.     char              *printcommand;    /* unused */
  111. } MailcapEntry;
  112.  
  113. MailcapEntry *mc_head = NULL,
  114.          *mc_tail = NULL;
  115. static int    mc_is_initialized = 0;
  116.  
  117.  
  118. /*
  119.  * Types used to pass parameters and operator functions to the
  120.  * mime.types searching routines.
  121.  */
  122. #define MT_MAX_FILE_EXTENSION 3
  123.  
  124. /*
  125.  * Struct passed mime.types search functions
  126.  */
  127. typedef struct {
  128.     union {
  129.     char        *ext;
  130.     char        *mtype;
  131.     } from;
  132.     union {
  133.     BODY        *body;
  134.     char        *ext;
  135.     } to;
  136. } MT_MAP_T;
  137.  
  138. typedef int (* MT_OPERATORPROC) PROTO((MT_MAP_T *, FILE *));
  139.  
  140.  
  141. /*
  142.  * Internal prototypes
  143.  */
  144. void      mc_addtolist PROTO((MailcapEntry *));
  145. char     *mc_bld_test_cmd PROTO((char *, int, char *, PARAMETER *));
  146. char     *mc_cmd_bldr PROTO((char *, int, char *, PARAMETER *, char *));
  147. int       mc_ctype_match PROTO((int, char *, char *));
  148. MailcapEntry *mc_get_command PROTO((int, char *, PARAMETER *));
  149. int       mc_get_entry PROTO((FILE *, MailcapEntry *));
  150. char     *mc_get_next_piece PROTO((char *, char **));
  151. void      mc_init PROTO((void));
  152. int       mc_passes_test PROTO((MailcapEntry *, int, char *, PARAMETER *));
  153. void      mc_process_file PROTO((char *));
  154. void      mt_get_file_ext PROTO((char *, char **));
  155. int      mt_srch_mime_type PROTO((MT_OPERATORPROC, MT_MAP_T *));
  156. int      mt_browse_types_file PROTO((MT_OPERATORPROC, MT_MAP_T *, char *));
  157. int      mt_translate_type PROTO((char *));
  158. int      mt_srch_by_ext PROTO((MT_MAP_T *, FILE *));
  159. int      mt_srch_by_type PROTO((MT_MAP_T *, FILE *));
  160.  
  161.  
  162.  
  163. /*
  164.  * mc_init - Run down the path gathering all the mailcap entries.
  165.  *           Returns with the Mailcap list built.
  166.  */
  167. void
  168. mc_init()
  169. {
  170.     char *s,
  171.      *pathcopy,
  172.      *path;
  173.   
  174.     if(mc_is_initialized)
  175.       return;
  176.  
  177.     dprint(2, (debugfile, "- mc_init -\n"));
  178.  
  179.     if(ps_global->VAR_MAILCAP_PATH)
  180.       /* there may need to be an override specific to pine */
  181.       pathcopy = cpystr(ps_global->VAR_MAILCAP_PATH);
  182.     else if((path = getenv("MAILCAPS")) != (char *)NULL)
  183.       /* rfc1524 specifies MAILCAPS as a path override */
  184.       pathcopy = cpystr(path);
  185.     else{
  186. #if defined(DOS) || defined(OS2)
  187.         /*
  188.      * This gets interesting.  Since we don't have any standard location
  189.      * for config/data files, look in the same directory as the PINERC
  190.      * and the same dir as PINE.EXE.  This is similar to the UNIX
  191.      * situation with personal config info coming before 
  192.      * potentially shared config data...
  193.      */
  194.     s = last_cmpnt(ps_global->pinerc);
  195.     if(s != NULL){
  196.         strncpy(tmp_20k_buf, ps_global->pinerc, s - ps_global->pinerc);
  197.         tmp_20k_buf[s - ps_global->pinerc] = '\0';
  198.     }
  199.     else
  200.       strcpy(tmp_20k_buf, ".\\");
  201.  
  202.     sprintf(tmp_20k_buf + strlen(tmp_20k_buf),
  203.             "MAILCAP%c%s\\MAILCAP",
  204.             MC_PATH_SEPARATOR,
  205.         ps_global->pine_dir);
  206. #else /* !DOS */
  207.     build_path(tmp_20k_buf, ps_global->home_dir, MC_STDPATH);
  208. #endif /* !DOS */
  209.     pathcopy = cpystr(tmp_20k_buf);
  210.     }
  211.  
  212.     path = pathcopy;            /* overloaded "path" */
  213.  
  214.     /*
  215.      * Insert an entry for the image-viewer variable from .pinerc, if present.
  216.      */
  217.     if(ps_global->VAR_IMAGE_VIEWER && *ps_global->VAR_IMAGE_VIEWER){
  218.     MailcapEntry mc;
  219.     char *p;
  220.  
  221.     p = tmp_20k_buf;
  222.     sstrcpy(&p, ps_global->VAR_IMAGE_VIEWER);
  223.     sstrcpy(&p, " %s");
  224.     mc.command       = cpystr(tmp_20k_buf);
  225.     mc.contenttype   = cpystr("image/*");
  226.     mc.testcommand   = (char *)NULL;
  227.     mc.printcommand  = (char *)NULL;
  228.     mc.needsterminal = 0;
  229.     mc.label         = cpystr("Pine Image Viewer");
  230.     mc.next          = (MailcapEntry *)NULL;
  231.  
  232.     mc_addtolist(&mc);
  233.     dprint(5, (debugfile, "mailcap: using image-viewer=%s\n", 
  234.            ps_global->VAR_IMAGE_VIEWER));
  235.     }
  236.  
  237.     dprint(7, (debugfile, "mailcap: path: %s\n", path));
  238.     while(path){
  239.     s = strindex(path, MC_PATH_SEPARATOR);
  240.     if(s)
  241.       *s++ = '\0';
  242.     mc_process_file(path);
  243.     path = s;
  244.     }
  245.   
  246.     if(pathcopy)
  247.       fs_give((void **)&pathcopy);
  248.  
  249.     mc_is_initialized++;
  250.  
  251. #ifdef DEBUG
  252.     if(debug >= 11){
  253.     MailcapEntry *mc;
  254.     int i = 0;
  255.  
  256.     dprint(11, (debugfile, "Collected mailcap entries\n"));
  257.     for(mc = mc_head; mc; mc = mc->next){
  258.  
  259.         dprint(11, (debugfile, "%d: ", i++));
  260.         if(mc->label)
  261.           dprint(11, (debugfile, "%s\n", mc->label));
  262.         if(mc->contenttype)
  263.           dprint(11, (debugfile, "   %s", mc->contenttype));
  264.         if(mc->command)
  265.           dprint(11, (debugfile, "   command: %s\n", mc->command));
  266.         if(mc->testcommand)
  267.           dprint(11, (debugfile, "   testcommand: %s", mc->testcommand));
  268.         if(mc->printcommand)
  269.           dprint(11, (debugfile, "   printcommand: %s", mc->printcommand));
  270.         dprint(11, (debugfile, "   needsterminal %d\n", mc->needsterminal));
  271.     }
  272.     }
  273. #endif /* DEBUG */
  274. }
  275.  
  276.  
  277. /*
  278.  * Add all the entries from this file onto the Mailcap list.
  279.  */
  280. void
  281. mc_process_file(file)
  282. char *file;
  283. {
  284.     MailcapEntry mc;
  285.     int file_check;
  286.     FILE *fp;
  287.     char filebuf[MAXPATH+1];
  288.   
  289.     dprint(2, (debugfile, "mailcap: process_file: %s\n", file));
  290.  
  291.     (void)strncpy(filebuf, file, MAXPATH);
  292.     filebuf[MAXPATH] = '\0';
  293.     file = fnexpand(filebuf, MAXPATH);
  294.     dprint(7, (debugfile, "mailcap: processing file: %s\n", file));
  295.     file_check = is_writable_dir(file);
  296.     switch(file_check){
  297.       case 0: case 1: /* is a directory */
  298.     dprint(2, (debugfile, "mailcap: %s is a directory, should be a file\n",
  299.         file));
  300.     return;
  301.  
  302.       case 2: /* ok */
  303.     break;
  304.  
  305.       case 3: /* doesn't exist */
  306.     dprint(5, (debugfile, "mailcap: %s doesn't exist\n", file));
  307.     return;
  308.  
  309.       default:
  310.     panic("Programmer botch in mc_process_file");
  311.     /*NOTREACHED*/
  312.     }
  313.  
  314.     fp = fopen(file, "r");
  315.  
  316.     if(fp){
  317.     while(mc_get_entry(fp, &mc))
  318.       mc_addtolist(&mc);
  319.     
  320.     (void)fclose(fp);
  321.     }
  322. }
  323.  
  324.  
  325. /*
  326.  * Find the next entry in the file pointed to by fp and fill in mc.
  327.  *
  328.  * Returns 1 if it found another entry, 0 if no more entries in file.
  329.  */
  330. int
  331. mc_get_entry(fp, mc)
  332. FILE *fp;
  333. MailcapEntry *mc;
  334. {
  335.     char *s, *t, *cont, *raw_entry;
  336.     char  linebuf[LINE_BUF_SIZE];
  337.     int   raw_entry_alloc, len;
  338.  
  339.     mc->contenttype   = (char *)NULL;
  340.     mc->command       = (char *)NULL;
  341.     mc->testcommand   = (char *)NULL;
  342.     mc->label         = (char *)NULL;
  343.     mc->printcommand  = (char *)NULL;
  344.     mc->needsterminal = 0;
  345.     mc->next          = (MailcapEntry *)NULL;
  346.  
  347. #define ALLOC_INCREMENT  1000
  348.     raw_entry_alloc = ALLOC_INCREMENT;
  349.     raw_entry = (char *)fs_get((size_t)raw_entry_alloc + 1);
  350. try_again:
  351.     *raw_entry = '\0';
  352.  
  353.     /* handle continuation lines and comments */
  354.     while(fgets(linebuf, LINE_BUF_SIZE, fp)){
  355.     if(*linebuf == '#' || *linebuf == '\n') /* skip comments and blanks */
  356.       continue;
  357.         len = strlen(linebuf);
  358.         if(linebuf[len-1] == '\n')
  359.       linebuf[--len] = '\0';
  360.         if(len + strlen(raw_entry) > raw_entry_alloc){
  361.             raw_entry_alloc += ALLOC_INCREMENT;
  362.             fs_resize((void **)&raw_entry, (size_t)raw_entry_alloc+1);
  363.         }
  364.         if(linebuf[len-1] == '\\'){
  365.             linebuf[len-1] = '\0';
  366.             (void)strcat(raw_entry, linebuf);
  367.         }
  368.     else{
  369.             (void)strcat(raw_entry, linebuf);
  370.             break;
  371.         }
  372.     }
  373.  
  374.     /* skip leading space in line */
  375.     for(cont = raw_entry; *cont && isspace((unsigned char)*cont); ++cont)
  376.       ;/* do nothing */
  377.     if(!*cont){  /* blank entry */
  378.     if(feof(fp)){  /* end of file */
  379.         if(raw_entry)
  380.           fs_give((void **)&raw_entry);
  381.         dprint(7, (debugfile, "mailcap: end of file\n"));
  382.         return 0;
  383.     }
  384.     goto try_again;
  385.     }
  386.  
  387.     s = strindex(cont, ';');
  388.     if(!s){
  389.     dprint(2, (debugfile, "mailcap: ignoring entry, no semicolon: %s\n",
  390.         raw_entry));
  391.     goto try_again;
  392.     }
  393.  
  394.     dprint(8, (debugfile, "mailcap: processing entry: %s\n", raw_entry));
  395.  
  396.     *s++ = '\0';
  397.  
  398.     /* trim white space, convert to lowercase */
  399.     mc->contenttype = cpystr(strclean(cont));
  400.     dprint(9, (debugfile, "mailcap: content_type: %s\n", mc->contenttype));
  401.  
  402.     /* make implicit wildcard explicit */
  403.     if (strindex(mc->contenttype, '/') == NULL){
  404.     fs_resize((void **)&mc->contenttype, strlen(mc->contenttype) + 3);
  405.         (void)strcat(mc->contenttype, "/*");
  406.     }
  407.  
  408.     t = mc_get_next_piece(s, &mc->command);
  409.     removing_leading_white_space(mc->command);
  410.     dprint(9, (debugfile, "mailcap: command: %s\n", mc->command));
  411.  
  412.     if(!t){
  413.     fs_give((void **)&raw_entry);
  414.     return 1;
  415.     }
  416.  
  417.     /* process fields */
  418.     dprint(9, (debugfile, "mailcap: flags: %s\n", t));
  419.     for(s = t; s; s = t){
  420.     char *arg, *eq;
  421.     
  422.     t = mc_get_next_piece(s, &arg);
  423.     if(*arg)
  424.       removing_leading_white_space(arg);
  425.     eq = strindex(arg, '=');
  426.     if(eq)
  427.       *eq++ = '\0';
  428.  
  429.     if(*arg)
  430.       removing_trailing_white_space(arg);
  431.     if(*arg){
  432.         if(!strucmp(arg, "needsterminal")){
  433.             mc->needsterminal = 1;
  434.             dprint(9, (debugfile, "mailcap: set needsterminal\n"));
  435.         }
  436.         else if(!strucmp(arg, "copiousoutput")){
  437.             mc->needsterminal = 2;
  438.             dprint(9, (debugfile, "mailcap: set copiousoutput\n"));
  439.         }
  440.         else if(eq && !strucmp(arg, "test")){
  441.             mc->testcommand = cpystr(eq);
  442.             dprint(9, (debugfile, "mailcap: testcommand=%s\n",
  443.             mc->testcommand));
  444.         }
  445.         else if(eq && !strucmp(arg, "description")){
  446.             mc->label = cpystr(eq);
  447.             dprint(9, (debugfile, "mailcap: label=%s\n",
  448.             mc->label));
  449.         }
  450.         else if(eq && !strucmp(arg, "print")){
  451.             mc->printcommand = cpystr(eq);
  452.             dprint(9, (debugfile, "mailcap: printcommand=%s\n",
  453.             mc->printcommand));
  454.         }
  455.         else if(eq && !strucmp(arg, "compose")){
  456.             ;/* not used */
  457.             dprint(9, (debugfile, "mailcap: not using compose=%s\n", eq));
  458.         }
  459.         else if(eq && !strucmp(arg, "composetyped")){
  460.             ;/* not used */
  461.             dprint(9, (debugfile, "mailcap: not using composetyped=%s\n",
  462.             eq));
  463.         }
  464.         else if(eq && !strucmp(arg, "textualnewlines")){
  465.             ;/* not used */
  466.             dprint(9, (debugfile,
  467.             "mailcap: not using texttualnewlines=%s\n", eq));
  468.         }
  469.         else if(eq && !strucmp(arg, "edit")){
  470.             ;/* not used */
  471.             dprint(9, (debugfile, "mailcap: not using edit=%s\n", eq));
  472.         }
  473.         else if(eq && !strucmp(arg, "x11-bitmap")){
  474.             ;/* not used */
  475.             dprint(9, (debugfile, "mailcap: not using x11-bitmap=%s\n",
  476.             eq));
  477.         }
  478.         else
  479.           dprint(9, (debugfile, "mailcap: ignoring unknown flag: %s\n",
  480.           arg));
  481.     }
  482.  
  483.     if(arg)
  484.       fs_give((void **)&arg);
  485.     }
  486.  
  487.     fs_give((void **)&raw_entry);
  488.     return 1;
  489. }
  490.  
  491.  
  492. /*
  493.  * s is a pointer to the current position in the mailcap entry
  494.  *
  495.  * "result" is a pointer to point at an alloc'd copy of the current piece, up
  496.  * to the next ";", and the return value is a pointer to the piece after
  497.  * the ";" (the next "s").
  498.  */
  499. char *
  500. mc_get_next_piece(s, result)
  501. char *s, **result;
  502. {
  503.     char *s2;
  504.     int quoted = 0;
  505.  
  506.     s2 = (char *)fs_get(strlen(s) * 2 + 1); /* absolute max, if all % signs */
  507.  
  508.     *result = s2;
  509.  
  510.     while(s && *s){
  511.     if(quoted){
  512.         if(*s == '%') /* quoted % turns into %% */
  513.           *s2++ = '%';
  514.       
  515.         *s2++ = *s++;
  516.         quoted = 0;
  517.     }
  518.     else{
  519.         if(*s == ';'){  
  520.         *s2 = '\0';
  521.         dprint(9, (debugfile, "mailcap: mc_get_next_piece: %s\n",
  522.             *result));
  523.         return(++s);
  524.         }
  525. #if defined(DOS) || defined(OS2)
  526.         /*
  527.          * RFC 1524 says that backslash is used to quote
  528.          * the next character, but since backslash is part of pathnames
  529.          * on DOS we're afraid people will not put double backslashes
  530.          * in their mailcap files.  Therefore, we violate the RFC by
  531.          * looking ahead to the next character.  If it looks like it
  532.          * is just part of a pathname, then we consider a single
  533.          * backslash to *not* be a quoting character, but a literal
  534.          * backslash instead.
  535.          */
  536.         else if(*s == '\\'){
  537.         int t;
  538.  
  539.         t = *(s+1);
  540.         /*
  541.          * If next char is any of these, treat the backslash
  542.          * that preceded it like a regular character.
  543.          */
  544.         if(t && isascii(t) &&
  545.              (isalnum((unsigned char)t) || t == '_' || t == '+'
  546.               || t == '-' || t == '=' || t == '~')){
  547.             *s2++ = *s++;
  548.         }
  549.         /* otherwise, treat it as a quoting backslash */
  550.         else{
  551.             quoted++;
  552.             ++s;
  553.         }
  554.         }
  555. #else  /* !DOS */
  556.         /* backslash quotes the next character */
  557.         else if(*s == '\\'){
  558.         quoted++;
  559.         ++s;
  560.         }
  561. #endif  /* !DOS */
  562.         else
  563.           *s2++ = *s++;
  564.     } 
  565.     }
  566.  
  567.     *s2 = '\0';
  568.  
  569.     dprint(9, (debugfile, "mailcap: mc_get_next_piece(last): %s\n", *result));
  570.  
  571.     return NULL;
  572. }
  573.      
  574.  
  575. /*
  576.  * Returns the mailcap entry for type/subtype from the successfull
  577.  * mailcap entry, or NULL if none.  Command string still contains % stuff.
  578.  */
  579. MailcapEntry *
  580. mc_get_command(type, subtype, params)
  581. int        type;
  582. char      *subtype;
  583. PARAMETER *params;
  584. {
  585.     MailcapEntry *mc;
  586.  
  587.     dprint(4, (debugfile, "- mc_get_command(%s/%s) -\n",
  588.     body_type_names(type), subtype));
  589.  
  590.     for(mc = mc_head; mc; mc = mc->next){
  591.     if(mc_ctype_match(type, subtype, mc->contenttype) &&
  592.        mc_passes_test(mc, type, subtype, params)){
  593.  
  594.         dprint(9, (debugfile, 
  595.              "mc_get_command: type=%s/%s, command=%s\n", 
  596.              body_type_names(type), subtype, mc->command));
  597.         return(mc);
  598.     }
  599.     }
  600.     return((MailcapEntry *)NULL);
  601. }
  602.  
  603.  
  604. /*
  605.  * Check whether the pattern "pat" matches this type/subtype.
  606.  * Returns 1 if it does, 0 if not.
  607.  */
  608. int
  609. mc_ctype_match(type, subtype, pat)
  610. int   type;
  611. char *subtype;
  612. char *pat;
  613. {
  614.     int  len, rv;
  615.     char *type_txt;
  616.     char *full_ctype;
  617.     char *p;
  618.  
  619.     type_txt = body_type_names(type);
  620.     len = strlen(subtype) + strlen(type_txt);
  621.     full_ctype = (char *)fs_get((size_t)len + 1 + 1);
  622.     p = full_ctype;
  623.     sstrcpy(&p, type_txt);
  624.     sstrcpy(&p, "/");
  625.     sstrcpy(&p, subtype);
  626.  
  627.     dprint(4, (debugfile, 
  628.        "mc_ctype_match: trying entry %s for %s\n", pat, full_ctype));
  629.         
  630.     rv = 0;
  631.  
  632.     /* check for exact match */
  633.     if(!strucmp(full_ctype, pat))
  634.       rv = 1;
  635.  
  636.     /* check for wildcard match */
  637.     else if ((len=strlen(pat)) >= 2 
  638.      && (pat[--len] == '*')
  639.          && (pat[--len] == '/')
  640.          && (!struncmp(full_ctype, pat, len))
  641.          && ((full_ctype[len] == '/') || (full_ctype[len] == '\0')))
  642.       rv = 1;
  643.  
  644.     fs_give((void **)&full_ctype);
  645.     return(rv);
  646. }
  647.  
  648.  
  649. /*
  650.  * Run the test command for entry mc to see if this entry currently applies to
  651.  * applies to this type/subtype.
  652.  *
  653.  * Returns 1 if it does pass test (exits with status 0), 0 otherwise.
  654.  */
  655. int
  656. mc_passes_test(mc, type, subtype, params)
  657. MailcapEntry *mc;
  658. int           type;
  659. char         *subtype;
  660. PARAMETER    *params;
  661. {
  662.     char *cmd = NULL;
  663.     int   rv;
  664.  
  665.     dprint(2, (debugfile, "- mc_passes_test -\n"));
  666.  
  667.     if(mc->testcommand && *mc->testcommand)
  668.     cmd = mc_bld_test_cmd(mc->testcommand, type, subtype, params);
  669.     
  670.     if(!mc->testcommand || !cmd || !*cmd){
  671.     if(cmd)
  672.       fs_give((void **)&cmd);
  673.     dprint(7, (debugfile, "no test command, so Pass\n"));
  674.     return 1;
  675.     }
  676.  
  677.     rv = exec_mailcap_test_cmd(cmd);
  678.     dprint(7, (debugfile, "mc_passes_test: \"%s\" %s (rv=%d)\n",
  679.        cmd, rv ? "Failed" : "Passed", rv)) ;
  680.  
  681.     fs_give((void **)&cmd);
  682.  
  683.     return(!rv);
  684. }
  685.  
  686.  
  687. int
  688. mailcap_can_display(type, subtype, params)
  689. int       type;
  690. char     *subtype;
  691. PARAMETER *params;
  692. {
  693.     dprint(5, (debugfile, "- mailcap_can_display -\n"));
  694.  
  695.     if(!mc_is_initialized)
  696.       mc_init();
  697.  
  698.     return(mc_get_command(type, subtype, params) ? 1 : 0);
  699. }
  700.  
  701.  
  702. char *
  703. mailcap_build_command(body, tmp_file, needsterm)
  704. BODY *body;
  705. char *tmp_file;
  706. int  *needsterm;
  707. {
  708.     MailcapEntry *mc;
  709.     char         *command;
  710.  
  711.     dprint(2, (debugfile, "- mailcap_build_command -\n"));
  712.  
  713.     if(!mc_is_initialized)
  714.       mc_init();
  715.  
  716.     mc = mc_get_command((int)body->type, body->subtype, body->parameter);
  717.     if(!mc){
  718.     q_status_message(SM_ORDER, 3, 4, "Error constructing viewer command");
  719.     dprint(2, (debugfile,
  720.            "mailcap_build_command: no command string for %s/%s\n",
  721.            body_type_names((int)body->type), body->subtype));
  722.     return((char *)NULL);
  723.     }
  724.  
  725.     *needsterm = mc->needsterminal;
  726.  
  727.     command = mc_cmd_bldr(mc->command, (int)body->type,
  728.               body->subtype, body->parameter, tmp_file);
  729.  
  730.     dprint(2, (debugfile, "built command: %s\n", command));
  731.  
  732.     return(command);
  733. }
  734.  
  735.  
  736. /*
  737.  * mc_bld_test_cmd - build the command to test if the given type flies
  738.  *
  739.  *    mc_cmd_bldr's tmp_file argument is NULL as we're not going to
  740.  *    decode and write each and every MIME segment's data to a temp file
  741.  *    when no test's going to use the data anyway.
  742.  */
  743. char *
  744. mc_bld_test_cmd(controlstring, type, subtype, params)
  745.     char      *controlstring;
  746.     int        type;
  747.     char      *subtype;
  748.     PARAMETER *params;
  749. {
  750.     return(mc_cmd_bldr(controlstring, type, subtype, params, NULL));
  751. }
  752.  
  753.  
  754. /*
  755.  * mc_cmd_bldr - construct a command string to execute
  756.  *
  757.  *    If tmp_file is null, then the contents of the given MIME segment
  758.  *    is not provided.  This is useful for building the "test=" string
  759.  *    as it doesn't operate on the segment's data.
  760.  *
  761.  *    The return value is an alloc'd copy of the command to be executed.
  762.  */
  763. char *
  764. mc_cmd_bldr(controlstring, type, subtype, parameter, tmp_file)
  765.     char *controlstring;
  766.     int   type;
  767.     char *subtype;
  768.     PARAMETER *parameter;
  769.     char *tmp_file;
  770. {
  771.     char        *from, *to, *s; 
  772.     int             prefixed = 0, used_tmp_file = 0;
  773.     PARAMETER   *parm;
  774.  
  775.     dprint(8, (debugfile, "- mc_cmd_bldr -\n"));
  776.  
  777.     for(from = controlstring, to = tmp_20k_buf; *from; ++from){
  778.     if(prefixed){            /* previous char was % */
  779.         prefixed = 0;
  780.         switch(*from){
  781.           case '%':            /* turned \% into this earlier */
  782.         *to++ = '%';
  783.         break;
  784.  
  785.           case 's':            /* insert tmp_file name in cmd */
  786.         if(tmp_file){
  787.             used_tmp_file = 1;
  788.             sstrcpy(&to, tmp_file);
  789.         }
  790.         else
  791.           dprint(2, (debugfile,
  792.                  "mc_cmd_bldr: %%s in cmd but not supplied!\n"));
  793.  
  794.         break;
  795.  
  796.           case 't':            /* insert MIME type/subtype */
  797.         /* quote to prevent funny business */
  798.         *to++ = '\'';
  799.         sstrcpy(&to, body_type_names(type));
  800.         *to++ = '/';
  801.         sstrcpy(&to, subtype);
  802.         *to++ = '\'';
  803.         break;
  804.  
  805.           case '{':            /* insert requested MIME param */
  806.         s = strindex(from, '}');
  807.         if(!s){
  808.             q_status_message1(SM_ORDER, 0, 4,
  809.     "Ignoring ill-formed parameter reference in mailcap file: %s", from);
  810.             break;
  811.         }
  812.  
  813.         *s = '\0';
  814.         ++from;    /* from is the part inside the brackets now */
  815.  
  816.         for(parm = parameter; parm; parm = parm->next)
  817.           if(!strucmp(from, parm->attribute))
  818.             break;
  819.  
  820.         dprint(9, (debugfile,
  821.                "mc_cmd_bldr: parameter %s = %s\n", 
  822.                from, parm ? parm->value : "(not found)"));
  823.  
  824.         /*
  825.          * Quote parameter values for /bin/sh.
  826.          * Put single quotes around the whole thing but every time
  827.          * there is an actual single quote put it outside of the
  828.          * single quotes with a backslash in front of it.  So the
  829.          * parameter value  fred's car
  830.          * turns into       'fred'\''s car'
  831.          */
  832.         *to++ = '\'';  /* opening quote */
  833.         if(parm){
  834.             char *p;
  835.  
  836.             /*
  837.              * Copy value, but quote single quotes for /bin/sh
  838.              * Backslash quote is ignored inside single quotes so
  839.              * have to put those outside of the single quotes.
  840.              */
  841.             for(p = parm->value; *p; p++){
  842.             if(*p == '\''){
  843.                 *to++ = '\'';  /* closing quote */
  844.                 *to++ = '\\';
  845.                 *to++ = '\'';  /* below will be opening quote */
  846.             }
  847.             *to++ = *p;
  848.             }
  849.         }
  850.  
  851.         *to++ = '\'';  /* closing quote for /bin/sh */
  852.  
  853.         *s = '}'; /* restore */
  854.         from = s;
  855.         break;
  856.  
  857.         /*
  858.          * %n and %F are used by metamail to support otherwise
  859.          * unrecognized multipart Content-Types.  Pine does
  860.          * not use these since we're only dealing with the individual
  861.          * parts at this point.
  862.          */
  863.           case 'n':
  864.           case 'F':  
  865.           default:  
  866.         dprint(9, (debugfile, 
  867.            "Ignoring %s format code in mailcap file: %%%c\n",
  868.            (*from == 'n' || *from == 'F') ? "unimplemented"
  869.                           : "unrecognized",
  870.            *from));
  871.         break;
  872.         }
  873.     }
  874.     else if(*from == '%')        /* next char is special */
  875.       prefixed = 1;
  876.     else                /* regular character, just copy */
  877.       *to++ = *from;
  878.     }
  879.  
  880.     *to = '\0';
  881.  
  882.     /*
  883.      * file not specified, redirect to stdin
  884.      */
  885.     if(!used_tmp_file && tmp_file)
  886.       sprintf(to, " < %s", tmp_file);
  887.  
  888.     return(cpystr(tmp_20k_buf));
  889.  
  890.  
  891. /*
  892.  * Adds element mc to the mailcap list.
  893.  */
  894. void
  895. mc_addtolist(mc)
  896. MailcapEntry *mc;
  897. {
  898.     register MailcapEntry *new_mc;
  899.  
  900.     new_mc = (MailcapEntry *)fs_get(sizeof(MailcapEntry));
  901.  
  902.     /*
  903.      * Copy the structure, but don't copy pointed-to strings, just
  904.      * copy the pointers themselves.
  905.      */
  906.     *new_mc = *mc;
  907.  
  908.     /* add to end of list so search order is correct */
  909.     if(mc_tail)
  910.     mc_tail->next = new_mc;
  911.     else
  912.     mc_head       = new_mc;
  913.  
  914.     mc_tail = new_mc;
  915. }
  916.  
  917.  
  918. void
  919. mailcap_free()
  920. {
  921.     MailcapEntry *mc, *mc_next;
  922.  
  923.     dprint(2, (debugfile, "- mailcap_free -\n"));
  924.  
  925.     if(!mc_is_initialized)
  926.       return;
  927.     
  928.     for(mc = mc_head; mc; mc = mc_next){
  929.     mc_next = mc->next;
  930.     if(mc->contenttype)
  931.     fs_give((void **)&mc->contenttype);
  932.     if(mc->command)
  933.         fs_give((void **)&mc->command);
  934.     if(mc->testcommand)
  935.         fs_give((void **)&mc->testcommand);
  936.     if(mc->label)
  937.         fs_give((void **)&mc->label);
  938.     if(mc->printcommand)
  939.         fs_give((void **)&mc->printcommand);
  940.     fs_give((void **)&mc);
  941.     }
  942.  
  943.     mc_is_initialized = 0;
  944.     mc_head = (MailcapEntry *)NULL;
  945.     mc_tail = (MailcapEntry *)NULL;
  946. }
  947.  
  948.  
  949.  
  950. /*
  951.  * END OF MAILCAP ROUTINES, START OF MIME.TYPES ROUTINES
  952.  */
  953.  
  954.  
  955. /*
  956.  * Exported function that does the work of sniffing the mime.types
  957.  * files and filling in the body pointer if found.  Returns 1 (TRUE) if
  958.  * extension found, and body pointer filled in, 0 (FALSE) otherwise.
  959.  */
  960. int
  961. set_mime_type_by_extension(body, filename)
  962.     BODY *body;
  963.     char *filename;
  964. {
  965.     char     *extension;
  966.     MT_MAP_T  e2b;
  967.  
  968.     mt_get_file_ext(filename, &extension);
  969.     dprint(5, (debugfile, "mt_set_new : filename=%s, extension=%s\n",
  970.            filename, extension));
  971.     if (*extension) {
  972.     e2b.from.ext = extension;
  973.     e2b.to.body = body;
  974.     return (mt_srch_mime_type(mt_srch_by_ext, &e2b));
  975.     }
  976.     return(0);
  977. }
  978.  
  979.  
  980. /*
  981.  * Exported function that maps from mime types to file extensions.
  982.  */
  983. int
  984. set_mime_extension_by_type (ext, mtype)
  985.     char *ext;
  986.     char *mtype;
  987. {
  988.     MT_MAP_T t2e;
  989.  
  990.     t2e.from.mtype = mtype;
  991.     t2e.to.ext = ext;
  992.     return (mt_srch_mime_type (mt_srch_by_type, &t2e));
  993. }
  994.     
  995.  
  996.  
  997.  
  998. /* 
  999.  * Separate and return a pointer to the first character in the 'filename'
  1000.  * character buffer that comes after the rightmost '.' character in the
  1001.  * filename. (What I mean is a pointer to the filename - extension).
  1002.  */
  1003. void
  1004. mt_get_file_ext(filename, extension)
  1005.     char *filename;
  1006.     char **extension;
  1007. {
  1008.     char *index = NULL;
  1009.  
  1010.     do
  1011.       if(*filename == '.')
  1012.     index = filename;
  1013.     while (*filename++ != '\0');
  1014.  
  1015.     *extension = (index) ? index + 1 : filename - 1;
  1016. }
  1017.  
  1018.  
  1019. /*
  1020.  * Build a list of possible mime.type files.  For each one that exists
  1021.  * call the mt_operator function.
  1022.  * Loop terminates when mt_operator returns non-zero.  
  1023.  */
  1024. int
  1025. mt_srch_mime_type(mt_operator, mt_map)
  1026.     MT_OPERATORPROC    mt_operator;
  1027.     MT_MAP_T           *mt_map;
  1028. {
  1029.     char *s, *pathcopy, *path;
  1030.     int   rv = 0;
  1031.   
  1032.     dprint(2, (debugfile, "- mt_use_mime_type -\n"));
  1033.  
  1034.     /* We specify MIMETYPES as a path override */
  1035.     if(ps_global->VAR_MIMETYPE_PATH)
  1036.       /* there may need to be an override specific to pine */
  1037.       pathcopy = cpystr(ps_global->VAR_MIMETYPE_PATH);
  1038.     else if((path = getenv("MIMETYPES")) != (char *)NULL)
  1039.       pathcopy = cpystr(path);
  1040.     else{
  1041. #if defined(DOS) || defined(OS2)
  1042.         /*
  1043.      * This gets interesting.  Since we don't have any standard location
  1044.      * for config/data files, look in the same directory as the PINERC
  1045.      * and the same dir as PINE.EXE.  This is similar to the UNIX
  1046.      * situation with personal config info coming before 
  1047.      * potentially shared config data...
  1048.      */
  1049.     s = last_cmpnt(ps_global->pinerc);
  1050.     if(s != NULL){
  1051.         strncpy(tmp_20k_buf, ps_global->pinerc, s - ps_global->pinerc);
  1052.         tmp_20k_buf[s - ps_global->pinerc] = '\0';
  1053.     }
  1054.     else
  1055.       strcpy(tmp_20k_buf, ".\\");
  1056.  
  1057.     sprintf(tmp_20k_buf + strlen(tmp_20k_buf),
  1058.             "%s%c%s\\%s",
  1059.         MT_USER_FILE, MT_PATH_SEPARATOR,
  1060.         ps_global->pine_dir, MT_USER_FILE);
  1061. #else    /* !DOS */
  1062.     build_path(tmp_20k_buf, ps_global->home_dir, MT_STDPATH);
  1063. #endif    /* !DOS */
  1064.     pathcopy = cpystr(tmp_20k_buf);
  1065.     }
  1066.  
  1067.     path = pathcopy;            /* overloaded "path" */
  1068.  
  1069.     dprint(7, (debugfile, "mime_types: path: %s\n", path));
  1070.     while(path){
  1071.     if(s = strindex(path, MT_PATH_SEPARATOR))
  1072.       *s++ = '\0';
  1073.  
  1074.     if(rv = mt_browse_types_file(mt_operator, mt_map, path))
  1075.       break;
  1076.  
  1077.     path = s;
  1078.     }
  1079.   
  1080.     if(pathcopy)
  1081.       fs_give((void **)&pathcopy);
  1082.  
  1083.     return(rv);
  1084. }
  1085.  
  1086.  
  1087. /*
  1088.  * Try to match a file extension against extensions found in the file
  1089.  * ``filename'' if that file exists. return 1 if a match
  1090.  * was found and 0 in all other cases.
  1091.  */
  1092. int
  1093. mt_browse_types_file(mt_operator, mt_map, filename)
  1094.     MT_OPERATORPROC    mt_operator;
  1095.     MT_MAP_T           *mt_map;
  1096.     char *filename;
  1097. {
  1098.     int   rv = 0;
  1099.     FILE *file;
  1100.  
  1101.     if(file = fopen(filename, "r")){
  1102.     rv = mt_operator(mt_map, file);
  1103.     fclose(file);
  1104.     }
  1105.     else
  1106.       dprint(1, (debugfile, "mt_browse: FAILED open(%s) : %s.\n",
  1107.          filename, error_description(errno)));
  1108.  
  1109.     return(rv);
  1110. }
  1111.  
  1112.  
  1113. /*
  1114.  * scan each line of the file. Treat each line as a mime type definition.
  1115.  * The first word is a type/subtype specification. All following words
  1116.  * are file extensions belonging to that type/subtype. Words are separated
  1117.  * bij whitespace characters.
  1118.  * If a file extension occurs more than once, then the first definition
  1119.  * determines the file type and subtype.
  1120.  */
  1121. int
  1122. mt_srch_by_ext(e2b, file)
  1123.     MT_MAP_T *e2b;
  1124.     FILE     *file;
  1125. {
  1126.     char buffer[LINE_BUF_SIZE];
  1127. #if    defined(DOS) || defined(OS2)
  1128. #define    STRCMP    strucmp
  1129. #else
  1130. #define    STRCMP    strcmp
  1131. #endif
  1132.  
  1133.     /* construct a loop reading the file line by line. Then check each
  1134.      * line for a matching definition.
  1135.      */
  1136.     while(fgets(buffer,LINE_BUF_SIZE,file) != NULL){
  1137.     char *typespec;
  1138.     char *try_extension;
  1139.  
  1140.     if(buffer[0] == '#')
  1141.       continue;        /* comment */
  1142.  
  1143.     /* remove last character from the buffer */
  1144.     buffer[strlen(buffer)-1] = '\0';
  1145.     /* divide the input buffer into words separated by whitespace.
  1146.      * The first words is the type and subtype. All following words
  1147.      * are file extensions.
  1148.      */
  1149.     dprint(5, (debugfile, "traverse: buffer=%s.\n", buffer));
  1150.     typespec = strtok(buffer," \t");    /* extract type,subtype  */
  1151.     dprint(5, (debugfile, "typespec=%s.\n", typespec));
  1152.     while((try_extension = strtok(NULL, " \t")) != NULL){
  1153.         /* compare the extensions, and assign the type if a match
  1154.          * is found.
  1155.          */
  1156.         dprint(5, (debugfile,"traverse: trying ext %s.\n", try_extension));
  1157.         if(STRCMP(try_extension, e2b->from.ext) == 0){
  1158.         /* split the 'type/subtype' specification */
  1159.         char *type = strtok(typespec,"/");
  1160.         char *subtype = strtok(NULL,"/");
  1161.         dprint(5, (debugfile, "traverse: type=%s, subtype=%s.\n",
  1162.                type,subtype));
  1163.         /* The type is encoded as a small integer. we have to
  1164.          * translate the character string naming the type into
  1165.          * the corresponding number.
  1166.          */
  1167.         e2b->to.body->type = mt_translate_type(type);
  1168.         e2b->to.body->subtype = cpystr(subtype);
  1169.         return 1; /* a match has been found */
  1170.         }
  1171.     }
  1172.     }
  1173.  
  1174.     dprint(5, (debugfile, "traverse: search failed.\n"));
  1175.     return 0;
  1176. }
  1177.  
  1178.  
  1179.  
  1180.  
  1181. /*
  1182.  * scan each line of the file. Treat each line as a mime type definition.
  1183.  * Here we are looking for a matching type.  When that is found return the
  1184.  * first extension that is three chars or less.
  1185.  */
  1186. int
  1187. mt_srch_by_type(t2e, file)
  1188.     MT_MAP_T *t2e;
  1189.     FILE     *file;
  1190. {
  1191.     char buffer[LINE_BUF_SIZE];
  1192.  
  1193.     /* construct a loop reading the file line by line. Then check each
  1194.      * line for a matching definition.
  1195.      */
  1196.     while(fgets(buffer,LINE_BUF_SIZE,file) != NULL){
  1197.     char *typespec;
  1198.     char *try_extension;
  1199.  
  1200.     if(buffer[0] == '#')
  1201.       continue;        /* comment */
  1202.  
  1203.     /* remove last character from the buffer */
  1204.     buffer[strlen(buffer)-1] = '\0';
  1205.     /* divide the input buffer into words separated by whitespace.
  1206.      * The first words is the type and subtype. All following words
  1207.      * are file extensions.
  1208.      */
  1209.     dprint(5, (debugfile, "traverse: buffer=%s.\n", buffer));
  1210.     typespec = strtok(buffer," \t");    /* extract type,subtype  */
  1211.     dprint(5, (debugfile, "typespec=%s.\n", typespec));
  1212.     if (strucmp (typespec, t2e->from.mtype) == 0) {
  1213.         while((try_extension = strtok(NULL, " \t")) != NULL) {
  1214.         if (strlen (try_extension) <= MT_MAX_FILE_EXTENSION) {
  1215.             strcpy (t2e->to.ext, try_extension);
  1216.             return (1);
  1217.             }
  1218.         }
  1219.         }
  1220.     }
  1221.  
  1222.     dprint(5, (debugfile, "traverse: search failed.\n"));
  1223.     return 0;
  1224. }
  1225.  
  1226.  
  1227. /*
  1228.  * Translate a character string representing a content type into a short 
  1229.  * integer number, according to the coding described in c-client/mail.h
  1230.  * List of content types taken from rfc1521, September 1993.
  1231.  */
  1232. int
  1233. mt_translate_type(type)
  1234.     char *type;
  1235. {
  1236.     int i;
  1237.  
  1238.     for (i=0;(i<=TYPEMAX) && body_types[i] && strucmp(type,body_types[i]);i++)
  1239.       ;
  1240.  
  1241.     if (i > TYPEMAX)
  1242.       i = TYPEOTHER;
  1243.     else if (!body_types[i])    /* if empty slot, assign it to this type */
  1244.       body_types[i] = cpystr (type);
  1245.  
  1246.     return(i);
  1247. }
  1248.